package comlib.fifo

import chisel3._
import chisel3.util._
import comlib.util.{groupSelect, groupMux}
import comlib.{Crossbar}



/** Shift Registers FIFO
 *
 * @param kind FIFO类型
 *   - bit[0]: FIFO内容类型
 *     - 0: Shift FIFO
 *     - 1: Mux FIFO
 *   - bit[1]: Shift FIFO子类型
 *     - 0: 输入DeMux，输出Reg
 *     - 1: 输入Reg，输出Mux
 * @param inFF 输入`enq/enq_data`是否需要打拍
 * @param outFF `deq`信号是否需要打拍
 * @param deqFF io.deq信号打拍控制
 *   - 0：不打拍
 *   - n: 打1拍，并拆分为n份（内部复制，用于控制扇出）。为了简化设计，第一个赋值寄存器仅用于valid信号的控制。
 *
 * Feature:
 *   - Enqueue into any position
 *   - Dequeue from head position and all other unit shift to head direction with 1 step
 *   - 时序特性
 *     - 输出均为寄存输出
 *     - 输入为DeMux输入
 *   
 */
class CFifo[T<:Data](gen:T, val size:Int=2, val pfullThd:Int=1,  
  hasLen:Boolean = false,
  bys:Boolean = false,
  inFF:Boolean = false,
  outFF:Boolean = false,
  deqFF:Int = 0,
  hasIntf:Boolean = false,
) extends Module {

  class CFifoIO extends Bundle{
    val enq = Input(Bool())
    val enq_data = Input(chiselTypeOf(gen))

    /** 出队使能。true.B: 使能 */
    val deq = Input(Bool())
    /** 当前FIFO头元素 */
    val deq_data = Output(chiselTypeOf(gen))
    /** FIFO头元素有效标识(非空标识)。 true.B - 有效 */
    val deq_valid = Output(Bool())

    /** FIFO完全满指示. true.B - 完全满 */
    val full = Output(Bool())
    /** 还差一个空就满标识 */
    val pre_full = Output(Bool())
    /** FIFO空标识 */
    val empty = Output(Bool())
    /** FIFO深度。0-空, 1-深度为1, .... */
    val length = Output(UInt(log2Up(size+1).W))
    /** FIFO内数据集合 */
    val data = Output(Vec(size, chiselTypeOf(gen)))

    /** 上溢错误 */
    val of = Output(UInt(1.W))
    /** 下溢错误 */
    val uf = Output(UInt(1.W))

  }

  val io = IO(new CFifoIO)
  
  val valids = RegInit( VecInit( Seq.fill(size)( false.B ) ) )
  val buf = Reg( Vec(size, chiselTypeOf(gen)) ) 
  val len = if(hasLen) RegInit( 0.U(log2Up(size+1).W) ) else null
  val i_deq = if( deqFF>0 ) Reg(Vec(deqFF, Bool())) else Wire(Vec(1, Bool()))

  i_deq.foreach{ case d => d := io.deq }

  when( i_deq(0) & io.enq ){  // Enqueue & Dequeue

    ( 0 until size-1 ).foreach { case p => buf(p) := buf(p+1) }   // Shift
    // buf(len-1.U) := io.enq_data
    ( 0 until size ).foreach { case p =>
      if( p == size-1 ) when( valids(p) ){ buf(p) := io.enq_data } 
      else when( valids(p) & (!valids(p+1)) ){ buf(p) := io.enq_data }
    }

  }.elsewhen( i_deq(0) ){ // Only Dequeue

    // 注意：对于ENQ_FF使能时，enq一定是使能的。该条件严格来说不会进入
    ( 0 until size-1 ).foreach { case p => buf(p) := buf(p+1) }       // Shift
    // valids(len-1.U) := false.B
    ( 0 until size ).foreach { case p => 
      if( p == size-1 ) valids(p) := false.B
      else valids(p) := valids(p+1) 
    }

    if( hasLen )
      len := len - 1.U

  }.elsewhen( io.enq ){

    // buf(len) := io.enq_data
    ( 0 until size ).foreach { case p =>
      if( p == 0 ) when( !valids(p) ){ buf(p) := io.enq_data } 
      else when( !valids(p) & valids(p-1) ){ buf(p) := io.enq_data }
    }
    // valids(len) := true.B
    ( size-1 to 0 by -1 ).foreach {case p =>
      if( p == 0 ) valids(p) := true.B 
      else valids(p) := valids(p-1)
    }
    if( hasLen ){
      len := len + 1.U
    }

  }

  io.empty := ~io.deq_valid
  io.full := valids((size-1).U)
  io.data := buf

  if( hasLen ) io.length := len   // 若需要输出Len，则输出，否则固定为0
  else io.length := 0.U

  val out_deq_valid = if( deqFF>0 ) Mux(i_deq(0), valids(1), valids(0)) else valids(0)

  val out_deq_data = if( deqFF>0 ){
    val width = gen.getWidth
    val fullDataSize = scala.math.ceil(width / (deqFF-1)).toInt
    val rmnDataSize = width - fullDataSize * (deqFF-2)
    val buf1 = buf(1).asUInt
    val buf0 = buf(0).asUInt

    val fullData = (deqFF-1 to 2 by -1).map { case s =>
      val mux_lsb = fullDataSize * (s-2) + rmnDataSize
      val mux_msb = mux_lsb + fullDataSize - 1
      val mux_in1 = buf1(mux_msb, mux_lsb)
      val mux_in0 = buf0(mux_msb, mux_lsb)
      Mux( i_deq(s), mux_in1, mux_in0 )
    }

    val rmnData = {
      val mux_in1 = buf1(rmnDataSize-1, 0)
      val mux_in0 = buf0(rmnDataSize-1, 0)
      Mux( i_deq(1), mux_in1, mux_in0 )
    }
    Cat( fullData :+ rmnData ).asTypeOf(gen)
  }else{
    buf(0)
  }

  val out_valid = if(outFF) RegInit(false.B) else Wire(Bool())
  val out_data = if(outFF) Reg(chiselTypeOf(gen)) else Wire(chiselTypeOf(gen))
  if( outFF ){
    when( io.deq & out_deq_valid ){
      out_valid := out_deq_valid
      out_data := out_deq_data
    }
  }else{
    out_valid := out_deq_valid
    out_data := out_deq_data
  }

  io.deq_valid := Mux(!out_valid&bys.B, io.enq, out_valid)
  io.deq_data :=  Mux(!out_valid&bys.B, io.enq_data, out_data)

  if( size <= pfullThd ) io.pre_full := io.full   // Corner：将满门限比FIFO深度都小
  else io.pre_full := valids((size-1-pfullThd).U)   // 正常的“将满”反压

  //=========================================    DFx    ====================================
  if( hasIntf ){
    val of_reg = RegInit(0.U(1.W))
    val uf_reg = RegInit(0.U(1.W))
    when( io.enq & valids(size-1) ){ of_reg := 1.U }
    when( io.deq & !io.deq_valid ){ uf_reg := 1.U }
    io.of := of_reg 
    io.uf := uf_reg
  }else{
    io.of := 0.U 
    io.uf := 0.U
  }

}


class SFifoCell[T<:Data](gen:T, nDeq:Int=1, nShift:Int=1, deqFF:Int=2) extends Module{

  class SFifoCellIO extends Bundle{
    val enq = Input(Bool())
    val enq_data = Input(chiselTypeOf(gen))
    val deqs = Input(Vec(if(deqFF==0) 2 else deqFF, Vec(nDeq, Bool())))
    val shift = Input(Bool())
    val shift_data = Input(Vec(nShift, chiselTypeOf(gen)))
    val shift_valid = Input(Vec(nShift, Bool()))
    val data = Output(chiselTypeOf(gen))
    val valid = Output(Bool())
    val deq_data = Output(chiselTypeOf(gen))
    val deq_valid = Output(Bool())
  }

  val io = IO(new SFifoCellIO)

  val buf = Reg(chiselTypeOf(gen))
  val valid = RegInit(false.B)

  val deqWid = log2Up(nDeq+1)
  val deqNums = io.deqs.map{ case ff => ff.map{ case d => Mux(d, 1.U(deqWid.W), 0.U(deqWid.W)) }.reduce(_+_) }
  val deqValids = io.deqs.map{ case ff => ff.reduce(_|_) }
  val shiftValid = io.shift_valid( deqNums(0) - 1.U )
  val shiftData = Crossbar.selectB( VecInit(deqNums.tail.map{case n => n-1.U}), io.shift_data, false ).out

  when( io.enq ){
    buf := io.enq_data
  }.elsewhen( io.shift ){
    buf := shiftData
  }

  when( io.enq ){
    valid := true.B
  }.elsewhen( io.shift ){
    valid := shiftValid
  }

  io.deq_valid := Mux( deqValids(0), shiftValid, valid )
  io.deq_data := groupMux( VecInit(deqValids.tail), shiftData, buf )

  io.data := buf 
  io.valid := valid
}


/** Shift FIFO
 * @param gen 存储数据格式
 * @param size FIFO深度
 * @param nDeq 读出端口个数
 * @param nShift 移位个数
 * @param preFullThd pre_full反压信号门限。门限为len距离size的大小
 * @param deqFF io.deqs信号打拍控制
 *   - 0：不打拍
 *   - n: 打1拍，并拆分为n份（内部复制，用于控制扇出）。为了简化设计，第一个赋值寄存器仅用于valid信号的控制。
 * @param debug Debug模式开启开关
 */
class SFifo[T<:Data](gen:T, val size:Int=2, val nDeq:Int=1, val nShift:Int=1, preFullThd:Int=1, deqFF:Int=0, debug:Boolean=false) extends Module{

  require(nDeq<=3, f"当前仅支持不大于3的nDeq")
  require(deqFF!=1, f"不能仅对valid打拍")

  class SFifoIO extends Bundle{
    val enq = Input(Bool())
    val enq_data = Input(chiselTypeOf(gen))

    val deqs = Input(Vec(nDeq, Bool()))
    val deq_data = Output(Vec(nDeq, chiselTypeOf(gen)))
    val deq_valid = Output(Vec(nDeq, Bool()))

    val len = Output(UInt(log2Up(size+1).W))
    val pre_full = Output(Bool())
    val of = Output(Bool())
    val uf = Output(Bool())
  }

  // 内部信号定义
  val io = IO(new SFifoIO)
  val cells = Seq.fill(size)(Module(new SFifoCell(gen, nDeq, nShift, deqFF=deqFF)))    // 内部结构
  val len = RegInit(0.U(log2Up(size+1).W))  // FIFO实时深度
  val of = RegInit(false.B)   // 上溢错误(已经满，却任然有入队请求)状态位
  val uf = RegInit(false.B)
  val i_deqs = if(deqFF>0) Reg(Vec(deqFF, Vec(nDeq, Bool()))) else Wire(Vec(2, Vec(nDeq, Bool())))

  i_deqs.foreach{ case d => d := io.deqs }

  val deqValid = i_deqs(0).reduce(_|_)  // Deq命令有效标识
  val deqNum = i_deqs(0).map{ case d => Mux(d, 1.U(log2Up(nDeq+1).W), 0.U(log2Up(nDeq+1).W)) }.reduce(_+_)    // Deq命令个数
  val valids = cells.map(_.io.valid)
  val invalids = cells.map(!_.io.valid)
  val shifts = (0 until size).map{ case c =>  // 当前节点在`deq`信号作用下，是否可以向前移动标识 
    if( c >= nDeq ) i_deqs(0).reduce(_|_)     // 非头节点，移动完全由头节点的`deq`信号决定 
    else i_deqs(0).slice(0,c+1).reduce(_|_)   // 头节点，是否可以前移条件：节点与其前面的头节点有`deq`信号
  }

  // 写入位置计算。Note：若deq作用到无效的位置，有可能把写入数据湮灭
  val enq_pos_oh = VecInit((0 until size).map{ case p =>  // 当前入队数据写入位置 
    if( p == 0 ) !valids(p)
    else !valids(p) & valids(p-1)   // 前一个位置有数据，当前位置无数据
  }).asUInt
  val enqs_pos_oh_ex = Mux( len===size.U, Cat(1.U(1.W), enq_pos_oh), Cat(0.U(1.W), enq_pos_oh) )  // 满覆盖最后一个。为了考虑满且有出队的场景
  val enqs = (enqs_pos_oh_ex >> deqNum)(size-1,0)   // 向前移动出队数据个数

  // 逐个内部单元赋值
  for( c <- 0 until size ){

    cells(c).io.deqs := i_deqs
    cells(c).io.shift := shifts(c)
    cells(c).io.enq := enqs(c)&io.enq
    cells(c).io.enq_data := io.enq_data

    val shift_data = ( 1 to nShift ).map{ case s =>
      if( c + s >= size ) 0.U.asTypeOf(gen)
      else cells( c + s ).io.data
    }
    cells(c).io.shift_data := VecInit(shift_data)

    val shift_valid = (1 to nShift).map{ case s => 
      if( c + s >= size ) false.B 
      else cells( c + s ).io.valid
    }
    cells(c).io.shift_valid := VecInit(shift_valid)

  }

  // 队列长度
  when(io.enq & deqValid ){
    len := len + 1.U - deqNum
  }.elsewhen(io.enq){
    len := len + 1.U
  }.elsewhen( deqValid ){
    len := len - deqNum
  }

  // 上溢中断处理
  when( len===size.U && io.enq ){ of := true.B }  // 上溢
  val uf_err = (0 until nDeq).map{ case o => i_deqs(0)(o) & !valids(o) }.reduce(_|_)
  when( uf_err ){ uf := true.B }

  // 输出接口
  for( o <- 0 until nDeq ){
    io.deq_valid(o) := cells(o).io.valid
    io.deq_data(o) := cells(o).io.data
  }
  io.len := len
  io.pre_full := len>=(size-preFullThd).U
  io.of := of
  io.uf := uf

  // Debug功能
  if( debug ){
    chisel3.printf("\n")
    chisel3.printf(p"  - len:    ${len}\n")
    chisel3.printf(p"  - data:   ${VecInit(cells.map(_.io.data))}\n")
    chisel3.printf(p"  - valids: ${VecInit(valids)}\n")
    chisel3.printf(p"  - enqs:   ${VecInit(enqs.asBools)}\n")
    chisel3.printf("\n")
  }
  

}

class FifoPrepare[D<:Data](gen:D) extends Module{

  class FifoPrepareIO extends Bundle{
    val ideq = Input(Bool())
    val ready = Input(Bool())
    val ivalid = Input(Bool())
    val idata = Input(chiselTypeOf(gen))
    val ovalid = Output(Bool())
    val odata = Output(chiselTypeOf(gen))
    val odeq = Output(Bool())
  }

  val io = IO(new FifoPrepareIO)
  val buf = Reg(chiselTypeOf(gen))
  val valid = RegInit(false.B)

  when( io.ideq & io.ready ){
    buf := io.idata
    valid := io.ivalid
    io.odeq := io.ivalid
  }.elsewhen( io.ideq ){
    valid := false.B
    io.odeq := false.B
  }.elsewhen( io.ready ){
    when( !valid ){
      buf := io.idata
      valid := io.ivalid
      io.odeq := io.ivalid
    }.otherwise{
      io.odeq := false.B
    }
  }.otherwise{
    io.odeq := false.B
  }

  io.ovalid := valid
  io.odata := buf
}

class FifoIFF[D<:Data](gen:D) extends Module{

  class FifoFFIO extends Bundle{
    val full = Input(Bool())
    val ienq = Input(Bool())
    val idata = Input(chiselTypeOf(gen))
    val oenq = Output(Bool())
    val odata = Output(chiselTypeOf(gen))
    val of = Output(Bool())
  }

  val io = IO(new FifoFFIO)

  val buf = Reg(chiselTypeOf(gen))
  val valid = RegInit(false.B)

  when( io.full & valid ){
    io.of := true.B
    io.oenq := false.B
  }.elsewhen( io.full ){
    io.of := false.B
    buf := io.idata 
    valid := io.ienq
    io.oenq := false.B
  }.elsewhen( valid ){
    io.of := false.B
    buf := io.idata 
    io.oenq := valid
    valid := io.ienq 
  }.otherwise{
    io.of := false.B
    io.oenq := false.B
  }

  io.odata := buf 

}


class CFifoEx[T<:Data](gen:T, val size:Int=2, val pfullThd:Int=1,  
  hasLen:Boolean = false,
  bys:Boolean = false,
  inFF:Boolean = false,
  outFF:Boolean = false,
  deqFF:Int = 0,
  hasIntf:Boolean = false,
  hasPrepare:Boolean = false,
) extends Module {

  class CFifoExIO extends Bundle{
    val enq = Input(Bool())
    val enq_data = Input(chiselTypeOf(gen))

    /** 出队使能。true.B: 使能 */
    val deq = Input(Bool())
    /** 当前FIFO头元素 */
    val deq_data = Output(chiselTypeOf(gen))
    /** FIFO头元素有效标识(非空标识)。 true.B - 有效 */
    val deq_valid = Output(Bool())

    /** FIFO完全满指示. true.B - 完全满 */
    val full = Output(Bool())
    /** 还差一个空就满标识 */
    val pre_full = Output(Bool())
    /** FIFO空标识 */
    val empty = Output(Bool())
    /** FIFO深度。0-空, 1-深度为1, .... */
    val length = Output(UInt(log2Up(size+1).W))
    /** FIFO内数据集合 */
    val data = Output(Vec(size, chiselTypeOf(gen)))

    /** 上溢错误 */
    val of = Output(UInt(1.W))
    /** 下溢错误 */
    val uf = Output(UInt(1.W))

    val ready = Input(Bool())
    val deq_valid_pre = Output(Bool())
    val deq_data_pre = Output(chiselTypeOf(gen))

  }

  val io = IO(new CFifoExIO)
  val fifo = Module(new CFifo( gen, size, pfullThd, hasLen, bys, inFF, outFF, deqFF, hasIntf ))
  val iFF = Module( new FifoIFF(gen) )
  val prepare = Module( new FifoPrepare(gen) )

  iFF.io.full := fifo.io.full
  iFF.io.ienq := io.enq 
  iFF.io.idata := io.enq_data 
   
  if( inFF ){
    fifo.io.enq := iFF.io.oenq 
    fifo.io.enq_data := iFF.io.odata
  }else{
    fifo.io.enq := io.enq 
    fifo.io.enq_data := io.enq_data 
  }

  if(hasPrepare){
    fifo.io.deq := prepare.io.odeq
  }else{
    fifo.io.deq := io.deq
  }
  
  prepare.io.ideq := io.deq 
  prepare.io.ready := io.ready 
  prepare.io.ivalid := fifo.io.deq_valid 
  prepare.io.idata := fifo.io.deq_data 

  if( hasPrepare ){
    io.deq_valid := prepare.io.ovalid
    io.deq_data := prepare.io.odata
  }else{
    io.deq_valid := fifo.io.deq_valid
    io.deq_data := fifo.io.deq_data
  }

  io.of := Mux( inFF.B, iFF.io.of, fifo.io.of )
  io.full := fifo.io.full & ( iFF.io.oenq | (!inFF).B )
  io.pre_full := fifo.io.pre_full
  io.empty := fifo.io.empty 
  io.length := fifo.io.length 
  io.data := fifo.io.data 
  io.uf := fifo.io.uf
  io.deq_valid_pre := fifo.io.deq_valid 
  io.deq_data_pre := fifo.io.deq_data
}